#include "algo-support-recorder.h"
#include <sstream>
#include <stdexcept>
#include <gtest/gtest.h>

typedef unsigned int uint;
using namespace std;

namespace cccn
{

GenerateTidyGraphProgressRecorder::GenerateTidyGraphProgressRecorder ()
  : m_db (0), m_dbname (),
  m_insertMainLoopTableSqlStmt (0), m_insertReconnectionLoopTableSqlStmt (0),
  m_iterationIndex (0), m_reconnectionIndex (0)
  {
  }

GenerateTidyGraphProgressRecorder::GenerateTidyGraphProgressRecorder (const char* dbname) 
  : m_db (0), m_dbname (),
  m_insertMainLoopTableSqlStmt (0), m_insertReconnectionLoopTableSqlStmt (0),
  m_iterationIndex (0), m_reconnectionIndex (0)
  {
  OpenDb (dbname);
  }

GenerateTidyGraphProgressRecorder::~GenerateTidyGraphProgressRecorder ()
  {
  uint status = sqlite3_finalize (m_insertMainLoopTableSqlStmt);
  SQLITE_STATUS_CHECK (status, "Cannot finalize the main-loop-table insert SQL statement: ");
  status = sqlite3_finalize (m_insertReconnectionLoopTableSqlStmt);
  SQLITE_STATUS_CHECK (status, "Cannot finalize the reconnection-loop-table insert SQL statement: ");
  status = sqlite3_close (m_db);
  SQLITE_STATUS_CHECK (status, "Cannot close database: ");
  }

void 
GenerateTidyGraphProgressRecorder::OpenDb (const char* dbname)
  {
  m_dbname = dbname;

  uint status = sqlite3_open (m_dbname.c_str (), &m_db);
  SQLITE_STATUS_CHECK (status, "Cannot open database: ");

  const char* sql_make_table = "create table if not exists main_loop_table (\
    id integer primary key asc autoincrement, \
    db_open_time text, \
    iteration_id integer, \
    out_time text, \
    out_queue_size integer, \
    root_graph_size integer, \
    root_graph_girth integer, \
    root_graph_sumgirth integer, \
    root_graph_avg_dist real, \
    root_graph_sync_value real, \
    root_graph_sync_ratio real, \
    root_graph_xgmml blob);";
  RunSql (sql_make_table);

  sql_make_table = "create table if not exists reconnection_loop_table (\
    id integer primary key asc autoincrement, \
    db_open_time text, \
    iteration_id integer, \
    reconnection_id integer, \
    reconnected_graph_size integer, \
    reconnected_graph_girth integer, \
    reconnected_graph_sumgirth integer, \
    reconnected_graph_avg_dist real, \
    reconnected_graph_sync_value real, \
    reconnected_graph_sync_ratio real, \
    reconnected_graph_xgmml blob);";
  RunSql (sql_make_table);  

  // prepare the insert SQL statement for the main_loop_table
  const char* insert_main_loop_sql = 
    "insert into main_loop_table (\
    db_open_time, \
    iteration_id, \
    out_time, \
    out_queue_size, \
    root_graph_size, \
    root_graph_girth, \
    root_graph_sumgirth, \
    root_graph_avg_dist, \
    root_graph_sync_value, \
    root_graph_sync_ratio, \
    root_graph_xgmml) values (\
    @dbOpenTime, \
    @iteration, \
    @outTime, \
    @outQueueSize, \
    @graphSize, \
    @graphGirth, \
    @graphSumgirth, \
    @graphAvgDist, \
    @graphSyncValue, \
    @graphSyncRatio, \
    @graphXgmml);";
  status = sqlite3_prepare_v2 (m_db, insert_main_loop_sql, strlen (insert_main_loop_sql), &m_insertMainLoopTableSqlStmt, NULL);
  SQLITE_STATUS_CHECK (status, "Cannot prepare the insert SQL statement for the main_loop_table: ");

  // prepare the insert SQL statement for the reconnection_main_loop_table
  const char* insert_reconnection_loop_sql =
    "insert into reconnection_loop_table (\
    db_open_time, \
    iteration_id, \
    reconnection_id, \
    reconnected_graph_size, \
    reconnected_graph_girth, \
    reconnected_graph_sumgirth, \
    reconnected_graph_avg_dist, \
    reconnected_graph_sync_value, \
    reconnected_graph_sync_ratio, \
    reconnected_graph_xgmml) values (\
    @dbOpenTime, \
    @iteration, \
    @reconnection, \
    @graphSize, \
    @graphGirth, \
    @graphSumgirth, \
    @graphAvgDist, \
    @graphSyncValue, \
    @graphSyncRatio, \
    @graphXgmml);";
  status = sqlite3_prepare_v2 (m_db, insert_reconnection_loop_sql, strlen (insert_reconnection_loop_sql), &m_insertReconnectionLoopTableSqlStmt, NULL);
  SQLITE_STATUS_CHECK (status, "Cannot prepare the insert SQL statement for the main_loop_table: ");

  // bind the database open time stamp to both prepared statements
  string dbOpenTime = GetDbDateTime ();
  const char* param = "@dbOpenTime";
  status = sqlite3_bind_text (m_insertMainLoopTableSqlStmt, 
    sqlite3_bind_parameter_index (m_insertMainLoopTableSqlStmt, param), 
    dbOpenTime.c_str (), dbOpenTime.size () * sizeof (char), SQLITE_TRANSIENT);
  SQLITE_STATUS_CHECK (status, "Cannot bind parameter to " << param << ": ");

  param = "@dbOpenTime";
  status = sqlite3_bind_text (m_insertReconnectionLoopTableSqlStmt, 
    sqlite3_bind_parameter_index (m_insertReconnectionLoopTableSqlStmt, param), 
    dbOpenTime.c_str (), dbOpenTime.size () * sizeof (char), SQLITE_TRANSIENT);
  SQLITE_STATUS_CHECK (status, "Cannot bind parameter to " << param << ": ");
  }

string 
GenerateTidyGraphProgressRecorder::GetDbDateTime ()
  {
  const char* sql_get_time = "select datetime ('now');";
  sqlite3_stmt* stmt_get_time;
  
  uint status = sqlite3_prepare_v2 (m_db, sql_get_time, strlen (sql_get_time), &stmt_get_time, NULL);
  SQLITE_STATUS_CHECK (status, "Cannot compile the SQL statement - " << sql_get_time << " - ");

  status = sqlite3_step (stmt_get_time);
  SQLITE_STATUS_CHECK (status, "Cannot step the SQL statement - " << sql_get_time << " - ");

  string date_time_str = (const char*) sqlite3_column_text (stmt_get_time, 0);

  status = sqlite3_finalize (stmt_get_time);
  SQLITE_STATUS_CHECK (status, "Cannot finalize excuting the SQL statement - " << sql_get_time << " - ");

  return date_time_str;
  }

void 
GenerateTidyGraphProgressRecorder::RunSql (const char* sql)
  {
  char* errmsg = NULL;
  uint status = sqlite3_exec (m_db, sql, NULL, NULL, &errmsg);
  SQLITE_STATUS_CHECK(status, "Cannot run the SQL statement - " << sql << " - (" << errmsg << "): ");

  if (!errmsg)
    {
    sqlite3_free (errmsg);
    }
  }

} // namespace cccn

/////////////
//         //
// Testing //
//         //
/////////////

using namespace cccn;
using namespace std;

TEST (GenerateTidyGraphProgressRecorderTest, GetDateTimeTest)
  {
  GenerateTidyGraphProgressRecorder recorder ("db.dat");

  string datetime = recorder.GetDbDateTime ();
  }